﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Net;
using Lidgren.Network;
using RebellionII.GameObjects.Map;
using RebellionII.GameObjects;
using RebellionII.GameObjects.Other;
using RebellionII.GameObjects.Units;
using RebellionII.GameObjects.Units.Instances;
using Units.GameObjects.Other;
using RebEngine.SceneObject.SceneGraph;

namespace RebEngine.Managers
{
    public class NetClientManager : GameComponent
    {
        public static NetClient Client { set; get; }
        public static NetPeerConfiguration config;
        public static bool ReadyToStart = false;

        public static Universe gameUniverse = new Universe();
        public static Dictionary<int, BaseUnit> UnitList = new Dictionary<int, BaseUnit>();

        public static MessageManager MessageManager = new MessageManager();
        public static GameSettings newSettings = new GameSettings(true);

        public static TeamName YourTeam { get; set; }
        private static bool IsInit = false;

        private static int gameTurn;

        public static int GameTurn
        {
            get { return NetClientManager.gameTurn; }
            set { NetClientManager.gameTurn = value; }
        }

        public NetClientManager(Game game) 
            : base (game)
        {
            config = new NetPeerConfiguration("Main");
            LogManager.Write("NetClient: Starting up...");
            config.EnableMessageType(NetIncomingMessageType.DiscoveryResponse);

            Client = new NetClient(config);
            Client.Start();

            YourTeam = TeamName.Default;

            gameUniverse = new Universe();

            Team[] tempTeams = new Team[5];

            tempTeams[0] = EngineManager.ContentManager.Load<Team>("Content/XML/Empire");
            tempTeams[1] = EngineManager.ContentManager.Load<Team>("Content/XML/Alliance");
            tempTeams[2] = EngineManager.ContentManager.Load<Team>("Content/XML/Hutts");
            tempTeams[3] = EngineManager.ContentManager.Load<Team>("Content/XML/Smugglers");
            tempTeams[4] = EngineManager.ContentManager.Load<Team>("Content/XML/Default");

            foreach (var team in tempTeams)
            {
                var fakeTeam = team.ConvertToDictionary();
                foreach (var temp in fakeTeam)
                {
                    UnitList.Add(temp.Key, temp.Value);
                }
            }
        }

        /// <summary>
        /// Runs through the UnitList and loads all textures and models named in the XML files
        /// </summary>
        public override void Initialize()
        {
 	        base.Initialize();
            foreach (var temp in UnitList)
            {   
                if(temp.Value is Troop)                  
                    TextureManager.AddTexture("Troop", temp.Value.sTextureName);
                if (temp.Value is CapitalShip)
                {
                    TextureManager.AddTexture("CapitalShip", temp.Value.sTextureName);
                    ModelManager.AddModel(((CapitalShip)temp.Value).sModelName);
                }
                if (temp.Value is Fighter)
                {
                    TextureManager.AddTexture("Fighter", temp.Value.sTextureName);
                    ModelManager.AddModel(((Fighter)temp.Value).sModelName);
                }
                if (temp.Value is Character)
                    TextureManager.AddTexture("Character", temp.Value.sTextureName);
                if (temp.Value is Facility)
                    TextureManager.AddTexture("Facility", temp.Value.sTextureName);
            }
        }

        /// <summary>
        /// Checks if the Client is connected and if it is, will continuously read message every update
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime)
        {
 	         base.Update(gameTime);

             if (IsInit)
             {
                 ReadMessage();
             }
        }

        /// <summary>
        /// Checks for message and reads based on what is sent
        /// </summary>
        public static void ReadMessage()
        {
            NetIncomingMessage msg;
            while ((msg = Client.ReadMessage()) != null)
            {
                switch (msg.MessageType)
                {
                    case NetIncomingMessageType.DiscoveryResponse:
                        LogManager.Write("Connected to " + msg.ReadString());
                        break;
                    case NetIncomingMessageType.DebugMessage:
                        break;
                    case NetIncomingMessageType.StatusChanged:
                        break;

                    case NetIncomingMessageType.Data:
                       int temp = msg.ReadInt32();
                        switch (temp)
                        {
                            case 99:
                                // All done, 99
                                ReadyToStart = true;
                                break;
                            case 0:
                                //Reading Generated Universe
                                #region Read Generated Universe
                                RebSystem newSystem = new RebSystem();
                                newSystem.iRebSystemID = msg.ReadInt32();
                                newSystem.sName = msg.ReadString();
                                newSystem.sTextureName = msg.ReadString();
                                newSystem.sSectorName = msg.ReadString();

                                int x = msg.ReadInt32();
                                int y = msg.ReadInt32();
                                newSystem.vPosition = new Vector2(x, y);
                                newSystem.bIsKeySystem = msg.ReadBoolean();

                                switch (msg.ReadInt32())
                                {
                                    case 0:
                                        newSystem.eSectorType = SectorType.Core;
                                        break;
                                    case 1:
                                        newSystem.eSectorType = SectorType.Mid;
                                        break;
                                    case 2:
                                        newSystem.eSectorType = SectorType.Rim;
                                        break;
                                }
                                newSystem.iLoyaltyAlliance = msg.ReadInt32();
                                newSystem.iLoyaltyEmpire = msg.ReadInt32();
                                newSystem.iLoyaltySystem = msg.ReadInt32();
                                newSystem.iNumEnergy = msg.ReadInt32();
                                newSystem.iNumFacilities = msg.ReadInt32();

                                switch (msg.ReadInt32())
                                {
                                    case 0:
                                        newSystem.CurrentOwner = TeamName.Empire;
                                        break;
                                    case 1:
                                        newSystem.CurrentOwner = TeamName.Alliance;
                                        break;
                                    case 2:
                                        newSystem.CurrentOwner = TeamName.Hutts;
                                        break;
                                    case 3:
                                        newSystem.CurrentOwner = TeamName.Smugglers;
                                        break;
                                    case 4:
                                        newSystem.CurrentOwner = TeamName.Default;
                                        break;
                                }

                                if (msg.ReadInt32() == 1)
                                    newSystem.eSpecials |= SystemSpecials.FacilityBonus;
                                if (msg.ReadInt32() == 1)
                                    newSystem.eSpecials |= SystemSpecials.ShipBonus;
                                if (msg.ReadInt32() == 1)
                                    newSystem.eSpecials |= SystemSpecials.TroopBonus;
                                if (msg.ReadInt32() == 1)
                                    newSystem.eSpecials |= SystemSpecials.JediBonus;

                                int check = msg.ReadInt32();
                                while (check == 1000)
                                {
                                    newSystem.lNeighbours.Add(msg.ReadInt32());
                                    check = msg.ReadInt32();
                                }
                                while (check == 1001)
                                {
                                    newSystem.lTroop.Add(new UnitInstance(msg.ReadInt32(), msg.ReadInt32()));
                                    check = msg.ReadInt32();
                                }
                                while (check == 1002)
                                {
                                    newSystem.lFacilities.Add(new UnitInstance(msg.ReadInt32(), msg.ReadInt32()));
                                    check = msg.ReadInt32();
                                }
                                while (check == 1003)
                                {
                                    newSystem.lFighter.Add(new UnitInstance(msg.ReadInt32(), msg.ReadInt32()));
                                    check = msg.ReadInt32();
                                }
                                gameUniverse.dUniverse.Add(newSystem.iRebSystemID, newSystem);
                                #endregion
                                break;
                            case 1:
                                //Tick
                                Tick();
                                gameTurn++;
                                break;
                            case 2:
                                //Receive New Unit
                                int planetID = msg.ReadInt32();
                                int refID = msg.ReadInt32();
                                int instID = msg.ReadInt32();
                                if (UnitList[refID] is Troop)
                                    gameUniverse.dUniverse[planetID].lTroop.Add(new UnitInstance(refID, instID));
                                if (UnitList[refID] is Fighter)
                                    gameUniverse.dUniverse[planetID].lFighter.Add(new UnitInstance(refID, instID));
                                if (UnitList[refID] is Facility)
                                    gameUniverse.dUniverse[planetID].lFacilities.Add(new UnitInstance(refID, instID));
                                break;
                            case 3:
                                //Receive New Fleet and CapitalShip
                                break;
                            case 4:
                                break;
                            case 5:
                                
                                break;
                            default:
                                break;
                        }
                        break;
                }
                Client.Recycle(msg);
            }
            
        }

        public static List<int> GetAvailableUnits(string type)
        {
            List<int> tempList = new List<int>();

            switch (type)
            {
                case "Facility":
                    foreach (var temp in NetClientManager.UnitList)
                    {
                        if (temp.Value is Facility)
                        {
                            if (((temp.Value.TheTeam == NetClientManager.YourTeam) && temp.Value.bIsEnabled) || ((temp.Value.TheTeam == TeamName.Default)&& temp.Value.bIsEnabled))
                            {
                                tempList.Add(temp.Key);
                            }
                        }
                    }
                    break;
                case "Ship":
                    foreach (var temp in NetClientManager.UnitList)
                    {
                        if (temp.Value is Fighter || temp.Value is CapitalShip)
                        {
                            if (((temp.Value.TheTeam == NetClientManager.YourTeam) && temp.Value.bIsEnabled) || ((temp.Value.TheTeam == TeamName.Default) && temp.Value.bIsEnabled))
                            {
                                tempList.Add(temp.Key);
                            }
                        }
                    }
                    break;
                case "Troop":
                    foreach (var temp in NetClientManager.UnitList)
                    {
                        if (temp.Value is Troop)
                        {
                            if (((temp.Value.TheTeam == NetClientManager.YourTeam) && temp.Value.bIsEnabled) || ((temp.Value.TheTeam == TeamName.Default) && temp.Value.bIsEnabled))
                            {
                                tempList.Add(temp.Key);
                            }
                        }
                    }
                    break;
            }
            return tempList;
        }
        /// <summary>
        /// Tick is called every time the server sends a tick call. All planets are updated for the player.
        /// </summary>
        private static void Tick()
        {
            foreach (var planet in gameUniverse.dUniverse)
            {
                if (planet.Value.FacilityBuildQuantity > 0)
                {
                    int facilityProcessRate = 0;
                    foreach (var temp in planet.Value.lFacilities)
                    {

                        if ((UnitList[temp.GetReferenceID] as Facility).eType == FacilityType.Facilities)
                        {
                            facilityProcessRate += (UnitList[temp.GetReferenceID] as Facility).iProcessRate;
                        }
                    }

                    planet.Value.FacilityBuildCurrentProgress += facilityProcessRate + planet.Value.FacilityBuildOverflow;
                    if (planet.Value.FacilityBuildCurrentProgress > planet.Value.FacilityBuildTotalCost)
                    {
                        planet.Value.bJustBuiltFacility = true;
                        planet.Value.FacilityBuildQuantity--;
                        planet.Value.FacilityBuildOverflow = planet.Value.FacilityBuildCurrentProgress - planet.Value.FacilityBuildTotalCost;
                        planet.Value.FacilityBuildCurrentProgress = planet.Value.FacilityBuildOverflow;
                    }
                }
                else
                {
                    planet.Value.ClearFacilityBuild();
                }

                if (planet.Value.TroopBuildQuantity > 0)
                {
                    int troopProcessRate = 0;
                    foreach (var temp in planet.Value.lFacilities)
                    {
                        if ((UnitList[temp.GetReferenceID] as Facility).eType == FacilityType.Troops)
                        {
                            troopProcessRate += (UnitList[temp.GetReferenceID] as Facility).iProcessRate;
                        }
                    }

                    planet.Value.TroopBuildCurrentProgress += troopProcessRate + planet.Value.TroopBuildOverflow;
                    if (planet.Value.TroopBuildCurrentProgress > planet.Value.TroopBuildTotalCost)
                    {
                        planet.Value.bJustBuiltTroop = true;
                        planet.Value.TroopBuildQuantity--;
                        planet.Value.TroopBuildOverflow = planet.Value.TroopBuildCurrentProgress - planet.Value.TroopBuildTotalCost;
                        planet.Value.TroopBuildCurrentProgress = planet.Value.FacilityBuildOverflow;
                    }
                }
                else
                {
                    planet.Value.ClearTroopBuild();
                }

                if (planet.Value.ShipBuildQuantity > 0)
                {
                    int shipProcessRate = 0;
                    foreach (var temp in planet.Value.lFacilities)
                    {
                        if ((UnitList[temp.GetReferenceID] as Facility).eType == FacilityType.Ships)
                        {
                            shipProcessRate += (UnitList[temp.GetReferenceID] as Facility).iProcessRate;
                        }
                    }

                    planet.Value.ShipBuildCurrentProgress += shipProcessRate + planet.Value.ShipBuildOverflow;
                    if (planet.Value.ShipBuildCurrentProgress > planet.Value.ShipBuildTotalCost)
                    {
                        planet.Value.bJustBuiltShip = true;
                        planet.Value.ShipBuildQuantity--;
                        planet.Value.ShipBuildOverflow = planet.Value.ShipBuildCurrentProgress - planet.Value.ShipBuildTotalCost;
                        planet.Value.ShipBuildCurrentProgress = planet.Value.ShipBuildOverflow;
                    }
                }
                else
                {
                    planet.Value.ClearShipBuild();
                }
                if (planet.Value.bJustBuiltShip)
                {
                    planet.Value.bJustBuiltShip = false;
                    if (UnitList[planet.Value.ShipBuildReferenceID] is Fighter)
                    {
                        UnitInstance temporaryInstance = new UnitInstance(planet.Value.ShipBuildReferenceID);
                        planet.Value.lFighter.Add(temporaryInstance);
                        SendCreatedUnit(planet.Key, temporaryInstance.GetReferenceID, temporaryInstance.GetInstanceID);
                    }
                    if (UnitList[planet.Value.ShipBuildReferenceID] is CapitalShip)
                    {
                        CapitalShipInstance temporaryInstance = new CapitalShipInstance(planet.Value.ShipBuildReferenceID);
                        Fleet newFleet = new Fleet();
                        newFleet.AddShips(temporaryInstance);
                        planet.Value.lFleet.Add(newFleet);

                        SendNewFleet(planet.Key, newFleet.iReferenceID, newFleet.iUniqueID, temporaryInstance.GetReferenceID, temporaryInstance.GetInstanceID);
                    }
                }

                if (planet.Value.bJustBuiltTroop)
                {
                    UnitInstance temporaryInstance = new UnitInstance(planet.Value.TroopBuildReferenceID);
                    planet.Value.bJustBuiltTroop = false;
                    planet.Value.lTroop.Add(temporaryInstance);
                    SendCreatedUnit(planet.Key, temporaryInstance.GetReferenceID, temporaryInstance.GetInstanceID);
                }

                if (planet.Value.bJustBuiltFacility)
                {
                    planet.Value.bJustBuiltFacility = false;

                    UnitInstance temporaryInstance = new UnitInstance(planet.Value.FacilityBuildReferenceID);

                    planet.Value.lFacilities.Add(temporaryInstance);
                    SendCreatedUnit(planet.Key, temporaryInstance.GetReferenceID, temporaryInstance.GetInstanceID);

                }
            }
        }

        private static void SendNewFleet(int p, int p_2, int p_3, int p_4, int p_5)
        {
            throw new NotImplementedException();
        }

        public static void SendCreatedUnit(int PlanetID, int ReferenceID, int Quantity) 
        {
            NetOutgoingMessage msg = Client.CreateMessage();
            msg.Write(1);
            msg.Write(PlanetID);
            msg.Write(ReferenceID);
            msg.Write(Quantity);

            Client.SendMessage(msg, NetDeliveryMethod.ReliableOrdered);
        }
        public static void SendCancelBuild(FacilityType eType, int PlanetID)
        {
            NetOutgoingMessage msg = Client.CreateMessage();
            msg.Write(2);
            msg.Write(PlanetID);
            msg.Write(eType.ToString());
            Client.SendMessage(msg, NetDeliveryMethod.ReliableOrdered);
        }

        public static void Connect(string host, int port)
        {
            Client.Connect(host, port);
            IsInit = true;
        }

        
    }
}

